//	---------------------------------------------------------------------------
//
//	@file		TwDirect3D10.cpp
//	@author		Philippe Decaudin
//	@license	This file is part of the AntTweakBar library.
//				For conditions of distribution and use, see License.txt
//
//	---------------------------------------------------------------------------

#if !defined ANT_TW_NO_D3D10

#include "TwPrecomp.h"
#include "TwDirect3D10.h"
#include "TwColors.h"

#include "d3d10vs2003.h" // Workaround to include D3D10.h with VS2003
#define D3D10_IGNORE_SDK_LAYERS // d3d10sdklayers.h may not exist
#include <d3d10.h>

namespace AntTweakBar {

using namespace std;

const char *g_ErrCantLoadD3D10	 = "Cannot load Direct3D10 library dynamically";
const char *g_ErrCompileFX10	 = "Direct3D10 effect compilation failed";
const char *g_ErrCreateFX10		 = "Direct3D10 effect creation failed";
const char *g_ErrTechNotFound10  = "Cannot find Direct3D10 technique effect";
const char *g_ErrCreateLayout10  = "Direct3D10 vertex layout creation failed";
const char *g_ErrCreateBuffer10  = "Direct3D10 vertex buffer creation failed";

//	---------------------------------------------------------------------------

// Dynamically loaded D3D10 functions (to avoid static linkage with d3d10.lib)
HMODULE g_D3D10Module = NULL;
typedef HRESULT (WINAPI *D3D10CompileEffectFromMemoryProc)(void *pData, SIZE_T DataLength, LPCSTR pSrcFileName, CONST D3D10_SHADER_MACRO *pDefines, ID3D10Include *pInclude, UINT HLSLFlags, UINT FXFlags, ID3D10Blob **ppCompiledEffect, ID3D10Blob **ppErrors);
typedef HRESULT (WINAPI *D3D10CreateEffectFromMemoryProc)(void *pData, SIZE_T DataLength, UINT FXFlags, ID3D10Device *pDevice, ID3D10EffectPool *pEffectPool, ID3D10Effect **ppEffect);
typedef HRESULT (WINAPI *D3D10StateBlockMaskEnableAllProc)(D3D10_STATE_BLOCK_MASK *pMask);
typedef HRESULT (WINAPI *D3D10CreateStateBlockProc)(ID3D10Device *pDevice, D3D10_STATE_BLOCK_MASK *pStateBlockMask, ID3D10StateBlock **ppStateBlock);
D3D10CompileEffectFromMemoryProc _D3D10CompileEffectFromMemory = NULL;
D3D10CreateEffectFromMemoryProc _D3D10CreateEffectFromMemory = NULL;
D3D10StateBlockMaskEnableAllProc _D3D10StateBlockMaskEnableAll = NULL;
D3D10CreateStateBlockProc _D3D10CreateStateBlock = NULL;

const RECT FullRect = {0, 0, 16000, 16000};
static bool RectIsFull(const RECT& r) { return r.left==FullRect.left && r.right==FullRect.right && r.top==FullRect.top && r.bottom==FullRect.bottom; }

static int LoadDirect3D10()
{
	if( g_D3D10Module!=NULL )
		return 1; // Direct3D10 library already loaded

	g_D3D10Module = LoadLibrary("D3D10.DLL");
	if( g_D3D10Module )
	{
		int res = 1;
		_D3D10CompileEffectFromMemory = reinterpret_cast<D3D10CompileEffectFromMemoryProc>(GetProcAddress(g_D3D10Module, "D3D10CompileEffectFromMemory"));
		if( _D3D10CompileEffectFromMemory==NULL )
			res = 0;
		_D3D10CreateEffectFromMemory = reinterpret_cast<D3D10CreateEffectFromMemoryProc>(GetProcAddress(g_D3D10Module, "D3D10CreateEffectFromMemory"));
		if( _D3D10CreateEffectFromMemory==NULL )
			res = 0;
		_D3D10StateBlockMaskEnableAll = reinterpret_cast<D3D10StateBlockMaskEnableAllProc>(GetProcAddress(g_D3D10Module, "D3D10StateBlockMaskEnableAll"));
		if( _D3D10StateBlockMaskEnableAll==NULL )
			res = 0;
		_D3D10CreateStateBlock = reinterpret_cast<D3D10CreateStateBlockProc>(GetProcAddress(g_D3D10Module, "D3D10CreateStateBlock"));
		if( _D3D10CreateStateBlock==NULL )
			res = 0;
		return res;
	}
	else
		return 0;	// cannot load DLL
}

static int UnloadDirect3D10()
{
	_D3D10CompileEffectFromMemory = NULL;
	_D3D10CreateEffectFromMemory =	NULL;
	_D3D10StateBlockMaskEnableAll = NULL;
	_D3D10CreateStateBlock = NULL;

	if( g_D3D10Module==NULL )
		return 1; // Direct3D10 library not loaded

	if( FreeLibrary(g_D3D10Module) )
	{
		g_D3D10Module = NULL;
		return 1;
	}
	else
		return 0; // cannot unload d3d10.dll
}

//	---------------------------------------------------------------------------

static ID3D10ShaderResourceView *BindFont(ID3D10Device *_Dev, ID3D10EffectShaderResourceVariable *_ResVar, const CTexFont *_Font)
{
	assert(_Font!=NULL);
	assert(_ResVar!=NULL);

	int w = _Font->m_TexWidth;
	int h = _Font->m_TexHeight;
	color32 *font32 = new color32[w*h];
	color32 *p = font32;
	for( int i=0; i<w*h; ++i, ++p )
		*p = 0x00ffffff | (((color32)(_Font->m_TexBytes[i]))<<24);

	D3D10_TEXTURE2D_DESC desc;
	desc.Width = w;
	desc.Height = h;
	desc.MipLevels = 1;
	desc.ArraySize = 1;
	desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	desc.SampleDesc.Count = 1;
	desc.SampleDesc.Quality = 0;
	desc.Usage = D3D10_USAGE_IMMUTABLE;
	desc.BindFlags = D3D10_BIND_SHADER_RESOURCE;
	desc.CPUAccessFlags = 0;
	desc.MiscFlags = 0;
	D3D10_SUBRESOURCE_DATA data;
	data.pSysMem = font32;
	data.SysMemPitch = w*sizeof(color32);
	data.SysMemSlicePitch = 0;
	ID3D10Texture2D *tex = NULL;
	ID3D10ShaderResourceView *texRV = NULL;
	if( SUCCEEDED(_Dev->CreateTexture2D(&desc, &data, &tex)) )
	{
		if( SUCCEEDED(_Dev->CreateShaderResourceView(tex, NULL, &texRV)) )
			if( _ResVar )
				_ResVar->SetResource(texRV);
		tex->Release();
		tex = NULL;
	}

	delete[] font32;
	return texRV;
}

//	---------------------------------------------------------------------------

static void UnbindFont(ID3D10Device *_Dev, ID3D10EffectShaderResourceVariable *_ResVar, ID3D10ShaderResourceView *_TexRV)
{
	(void)_Dev;

	if( _ResVar )
		_ResVar->SetResource(NULL);

	if( _TexRV )
	{
		ULONG rc = _TexRV->Release();
		assert( rc==0 ); (void)rc;
	}
}

//	---------------------------------------------------------------------------

struct CState10
{
	ID3D10StateBlock *	m_StateBlock;

	void			Save();
	void			Restore();
					CState10(ID3D10Device *_Dev);
					~CState10();
private:
	ID3D10Device *	m_D3DDev;
};

CState10::CState10(ID3D10Device *_Dev)
{
	ZeroMemory(this, sizeof(CState10));
	m_D3DDev = _Dev;
}

CState10::~CState10()
{
	if( m_StateBlock )
	{
		UINT rc = m_StateBlock->Release();
		assert( rc==0 ); (void)rc;
		m_StateBlock = NULL;
	}
}

void CState10::Save()
{
	if( !m_StateBlock )
	{
		D3D10_STATE_BLOCK_MASK stateMask;
		_D3D10StateBlockMaskEnableAll(&stateMask);
		_D3D10CreateStateBlock(m_D3DDev, &stateMask, &m_StateBlock);
	}

	if( m_StateBlock )
		m_StateBlock->Capture();
}

void CState10::Restore()
{
	if( m_StateBlock )
		m_StateBlock->Apply();
}

//	---------------------------------------------------------------------------

char g_ShaderFX[] = "// AntTweakBar shaders and techniques \n"
	" float4 g_Offset = 0; float4 g_CstColor = 1; \n"
	" struct LineRectPSInput { float4 Pos : SV_POSITION; float4 Color : COLOR0; }; \n"
	" LineRectPSInput LineRectVS(float4 pos : POSITION, float4 color : COLOR, uniform bool useCstColor) { \n"
	"	LineRectPSInput ps; ps.Pos = pos + g_Offset; \n"
	"	ps.Color = useCstColor ? g_CstColor : color; return ps; } \n"
	" float4 LineRectPS(LineRectPSInput input) : SV_Target { return input.Color; } \n"
	" technique10 LineRect { pass P0 { \n"
	"	SetVertexShader( CompileShader( vs_4_0, LineRectVS(false) ) ); \n"
	"	SetGeometryShader( NULL ); \n"
	"	SetPixelShader( CompileShader( ps_4_0, LineRectPS() ) ); \n"
	" } }\n"
	" technique10 LineRectCstColor { pass P0 { \n"
	"	SetVertexShader( CompileShader( vs_4_0, LineRectVS(true) ) ); \n"
	"	SetGeometryShader( NULL ); \n"
	"	SetPixelShader( CompileShader( ps_4_0, LineRectPS() ) ); \n"
	" } }\n"
	" Texture2D Font; \n"
	" SamplerState FontSampler { Filter = MIN_MAG_MIP_POINT; AddressU = BORDER; AddressV = BORDER; BorderColor=float4(0, 0, 0, 0); }; \n"
	" struct TextPSInput { float4 Pos : SV_POSITION; float4 Color : COLOR0; float2 Tex : TEXCOORD0; }; \n"
	" TextPSInput TextVS(float4 pos : POSITION, float4 color : COLOR, float2 tex : TEXCOORD0, uniform bool useCstColor) { \n"
	"	TextPSInput ps; ps.Pos = pos + g_Offset; \n"
	"	ps.Color = useCstColor ? g_CstColor : color; ps.Tex = tex; return ps; } \n"
	" float4 TextPS(TextPSInput input) : SV_Target { return Font.Sample(FontSampler, input.Tex)*input.Color; } \n"
	" technique10 Text { pass P0 { \n"
	"	SetVertexShader( CompileShader( vs_4_0, TextVS(false) ) ); \n"
	"	SetGeometryShader( NULL ); \n"
	"	SetPixelShader( CompileShader( ps_4_0, TextPS() ) ); \n"
	" } }\n"
	" technique10 TextCstColor { pass P0 { \n"
	"	SetVertexShader( CompileShader( vs_4_0, TextVS(true) ) ); \n"
	"	SetGeometryShader( NULL ); \n"
	"	SetPixelShader( CompileShader( ps_4_0, TextPS() ) ); \n"
	" } }\n"
	" // End of AntTweakBar shaders and techniques \n";

//	---------------------------------------------------------------------------

void* CTwGraphDirect3D10::DetectDevice(IUnknown* dev)
{
	ID3D10Device* result = NULL;
	static _GUID IID_ID3D10Device_local = {
			0x9B7E4C0F, 0x342C, 0x4106, { 0xA1, 0x9F, 0x4F, 0x27, 0x04, 0xF6, 0x89, 0xF0 } };
	dev->QueryInterface(IID_ID3D10Device_local, (void**)&result);
	return result;
}

CTwGraphDirect3D10::CTwGraphDirect3D10(void* _D3DDevice)
{
	m_D3DDev = static_cast<ID3D10Device *>(_D3DDevice);
	m_D3DDevInitialRefCount = m_D3DDev->AddRef() - 1;

	m_Drawing = false;
	m_OffsetX = m_OffsetY = 0;
	m_ViewportInit = new D3D10_VIEWPORT;
	m_FontTex = NULL;
	m_FontD3DTexRV = NULL;
	m_WndWidth = 0;
	m_WndHeight = 0;
	m_State = NULL;
	m_DepthStencilState = NULL;
	m_BlendState = NULL;
	m_RasterState = NULL;
	m_RasterStateAntialiased = NULL;
	m_RasterStateCullCW = NULL;
	m_RasterStateCullCCW = NULL;
	m_Effect = NULL;
	m_LineRectTech = NULL;
	m_LineRectCstColorTech = NULL;
	m_LineRectVertexLayout = NULL;
	m_LineVertexBuffer = NULL;
	m_RectVertexBuffer = NULL;
	m_TrianglesVertexBuffer = NULL;
	m_TrianglesVertexBufferCount = 0;
	m_TextTech = NULL;
	m_TextCstColorTech = NULL;
	m_TextVertexLayout = NULL;
	m_FontD3DResVar = NULL;
	m_OffsetVar = NULL;
	m_CstColorVar = NULL;
}

int CTwGraphDirect3D10::Init()
{
	// Load some D3D10 functions
	if( !LoadDirect3D10() )
	{
		g_ErrorState = g_ErrCantLoadD3D10;
		return 0;
	}

	// Allocate state object
	m_State = new CState10(m_D3DDev);

	// Compile shaders
	DWORD shaderFlags = D3D10_SHADER_ENABLE_STRICTNESS;
	ID3D10Blob *compiledFX = NULL;
	ID3D10Blob *errors = NULL;
	HRESULT hr = _D3D10CompileEffectFromMemory(g_ShaderFX, strlen(g_ShaderFX), "AntTweakBarFX", NULL, NULL, shaderFlags, 0, &compiledFX, &errors);
	if( FAILED(hr) )
	{
		const size_t ERR_MSG_MAX_LEN = 4096;
		static char s_ErrorMsg[ERR_MSG_MAX_LEN]; // must be static - no free
		strncpy(s_ErrorMsg, g_ErrCompileFX10, ERR_MSG_MAX_LEN-1);
		size_t errOffset = strlen(s_ErrorMsg);
		size_t errLen = 0;
		if( errors!=NULL )
		{
			s_ErrorMsg[errOffset++] = ':';
			s_ErrorMsg[errOffset++] = '\n';
			errLen = min(errors->GetBufferSize(), ERR_MSG_MAX_LEN-errOffset-2);
			strncpy(s_ErrorMsg+errOffset, static_cast<char *>(errors->GetBufferPointer()), errLen);
			errors->Release();
			errors = NULL;
		}
		s_ErrorMsg[errOffset+errLen] = '\0';
		g_ErrorState = s_ErrorMsg;
		return 0;
	}
	hr = _D3D10CreateEffectFromMemory(compiledFX->GetBufferPointer(), compiledFX->GetBufferSize(), 0, m_D3DDev, NULL, &m_Effect);
	compiledFX->Release();
	if( FAILED(hr) )
	{
		g_ErrorState = g_ErrCreateFX10;
		return 0;
	}

	// Obtain the techniques
	m_LineRectTech = m_Effect->GetTechniqueByName("LineRect");
	m_LineRectCstColorTech = m_Effect->GetTechniqueByName("LineRectCstColor");
	m_TextTech = m_Effect->GetTechniqueByName("Text");
	m_TextCstColorTech = m_Effect->GetTechniqueByName("TextCstColor");
	if( m_LineRectTech==NULL || m_TextTech==NULL || m_LineRectCstColorTech==NULL || m_TextCstColorTech==NULL )
	{
		g_ErrorState = g_ErrTechNotFound10;
		return 0;
	}

	// Create input layout for lines & rect
	D3D10_INPUT_ELEMENT_DESC lineRectLayout[] =
	{
		{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
		{ "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, offsetof(CLineRectVtx, m_Color), D3D10_INPUT_PER_VERTEX_DATA, 0 }
	};
	D3D10_PASS_DESC passDesc;
	hr = m_LineRectTech->GetPassByIndex(0)->GetDesc(&passDesc);
	if( SUCCEEDED(hr) )
		hr = m_D3DDev->CreateInputLayout(lineRectLayout, sizeof(lineRectLayout)/sizeof(lineRectLayout[0]), passDesc.pIAInputSignature, passDesc.IAInputSignatureSize, &m_LineRectVertexLayout);
	if( FAILED(hr) )
	{
		g_ErrorState = g_ErrCreateLayout10;
		return 0;
	}

	// Create line vertex buffer
	D3D10_BUFFER_DESC bd;
	bd.Usage = D3D10_USAGE_DYNAMIC;
	bd.ByteWidth = 2 * sizeof(CLineRectVtx);
	bd.BindFlags = D3D10_BIND_VERTEX_BUFFER;
	bd.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
	bd.MiscFlags = 0;
	hr = m_D3DDev->CreateBuffer(&bd, NULL, &m_LineVertexBuffer);
	if( FAILED(hr) )
	{
		g_ErrorState = g_ErrCreateBuffer10;
		return 0;
	}

	// Create rect vertex buffer
	bd.ByteWidth = 4 * sizeof(CLineRectVtx);
	hr = m_D3DDev->CreateBuffer(&bd, NULL, &m_RectVertexBuffer);
	if( FAILED(hr) )
	{
		g_ErrorState = g_ErrCreateBuffer10;
		return 0;
	}

	// Create input layout for text
	D3D10_INPUT_ELEMENT_DESC textLayout[] =
	{
		{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
		{ "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, offsetof(CTextVtx, m_Color), D3D10_INPUT_PER_VERTEX_DATA, 0 },
		{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(CTextVtx, m_UV), D3D10_INPUT_PER_VERTEX_DATA, 0 }
	};
	hr = m_TextTech->GetPassByIndex(0)->GetDesc(&passDesc);
	if( SUCCEEDED(hr) )
		hr = m_D3DDev->CreateInputLayout(textLayout, sizeof(textLayout)/sizeof(textLayout[0]), passDesc.pIAInputSignature, passDesc.IAInputSignatureSize, &m_TextVertexLayout);
	if( FAILED(hr) )
	{
		g_ErrorState = g_ErrCreateLayout10;
		return 0;
	}

	// Create depth stencil state object
	D3D10_DEPTH_STENCILOP_DESC od;
	od.StencilFunc = D3D10_COMPARISON_ALWAYS;
	od.StencilFailOp = D3D10_STENCIL_OP_KEEP;
	od.StencilPassOp = D3D10_STENCIL_OP_KEEP;
	od.StencilDepthFailOp = D3D10_STENCIL_OP_KEEP;
	D3D10_DEPTH_STENCIL_DESC dsd;
	dsd.DepthEnable = FALSE;
	dsd.DepthWriteMask = D3D10_DEPTH_WRITE_MASK_ZERO;
	dsd.DepthFunc = D3D10_COMPARISON_ALWAYS;
	dsd.StencilEnable = FALSE;
	dsd.StencilReadMask = D3D10_DEFAULT_STENCIL_READ_MASK;
	dsd.StencilWriteMask = D3D10_DEFAULT_STENCIL_WRITE_MASK;
	dsd.FrontFace = od;
	dsd.BackFace = od;
	m_D3DDev->CreateDepthStencilState(&dsd, &m_DepthStencilState);

	// Create blend state object
	D3D10_BLEND_DESC bsd;
	bsd.AlphaToCoverageEnable = FALSE;
	for(int i=0; i<8; ++i)
	{
		bsd.BlendEnable[i] = TRUE;
		bsd.RenderTargetWriteMask[i] = D3D10_COLOR_WRITE_ENABLE_ALL;
	}
	bsd.SrcBlend = D3D10_BLEND_SRC_ALPHA;
	bsd.DestBlend = D3D10_BLEND_INV_SRC_ALPHA;
	bsd.BlendOp =  D3D10_BLEND_OP_ADD;
	bsd.SrcBlendAlpha = D3D10_BLEND_SRC_ALPHA;
	bsd.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA;
	bsd.BlendOpAlpha = D3D10_BLEND_OP_ADD;
	m_D3DDev->CreateBlendState(&bsd, &m_BlendState);

	// Create rasterizer state object
	D3D10_RASTERIZER_DESC rd;
	rd.FillMode = D3D10_FILL_SOLID;
	rd.CullMode = D3D10_CULL_NONE;
	rd.FrontCounterClockwise = true;
	rd.DepthBias = false;
	rd.DepthBiasClamp = 0;
	rd.SlopeScaledDepthBias = 0;
	rd.DepthClipEnable = false;
	rd.ScissorEnable = true;
	rd.MultisampleEnable = false;
	rd.AntialiasedLineEnable = false;
	m_D3DDev->CreateRasterizerState(&rd, &m_RasterState);

	rd.AntialiasedLineEnable = true;
	m_D3DDev->CreateRasterizerState(&rd, &m_RasterStateAntialiased);
	rd.AntialiasedLineEnable = false;

	rd.CullMode = D3D10_CULL_BACK;
	m_D3DDev->CreateRasterizerState(&rd, &m_RasterStateCullCW);

	rd.CullMode = D3D10_CULL_FRONT;
	m_D3DDev->CreateRasterizerState(&rd, &m_RasterStateCullCCW);

	m_ViewportAndScissorRects[0] = FullRect;
	m_ViewportAndScissorRects[1] = FullRect;
	m_D3DDev->RSSetScissorRects(1, m_ViewportAndScissorRects);

	// Get effect globals
	if( m_Effect->GetVariableByName("Font") )
		m_FontD3DResVar = m_Effect->GetVariableByName("Font")->AsShaderResource();
	assert( m_FontD3DResVar!=NULL );
	if( m_Effect->GetVariableByName("g_Offset") )
		m_OffsetVar = m_Effect->GetVariableByName("g_Offset")->AsVector();
	assert( m_OffsetVar!=NULL );
	if( m_Effect->GetVariableByName("g_CstColor") )
		m_CstColorVar = m_Effect->GetVariableByName("g_CstColor")->AsVector();
	assert( m_CstColorVar!=NULL );

	return 1;
}

//	---------------------------------------------------------------------------

int CTwGraphDirect3D10::Shut()
{
	assert(m_Drawing==false);

	UnbindFont(m_D3DDev, m_FontD3DResVar, m_FontD3DTexRV);
	m_FontD3DTexRV = NULL;
	if( m_State )
	{
		delete m_State;
		m_State = NULL;
	}
	if( m_ViewportInit )
	{
		delete m_ViewportInit;
		m_ViewportInit = NULL;
	}

	if( m_DepthStencilState )
	{
		ULONG rc = m_DepthStencilState->Release();
		//assert( rc==0 ); // no assert: the client can use a similar (then shared) state
		(void)rc;
		m_DepthStencilState = NULL;
	}
	if( m_BlendState )
	{
		ULONG rc = m_BlendState->Release();
		//assert( rc==0 ); // no assert: the client can use a similar (then shared) state
		(void)rc;
		m_BlendState = NULL;
	}
	if( m_RasterState )
	{
		ULONG rc = m_RasterState->Release();
		//assert( rc==0 ); // no assert: the client can use a similar (then shared) state
		(void)rc;
		m_RasterState = NULL;
	}
	if( m_RasterStateAntialiased )
	{
		ULONG rc = m_RasterStateAntialiased->Release();
		//assert( rc==0 ); // no assert: the client can use a similar (then shared) state
		(void)rc;
		m_RasterStateAntialiased = NULL;
	}
	if( m_RasterStateCullCW )
	{
		ULONG rc = m_RasterStateCullCW->Release();
		//assert( rc==0 ); // no assert: the client can use a similar (then shared) state
		(void)rc;
		m_RasterStateCullCW = NULL;
	}
	if( m_RasterStateCullCCW )
	{
		ULONG rc = m_RasterStateCullCCW->Release();
		//assert( rc==0 ); // no assert: the client can use a similar (then shared) state
		(void)rc;
		m_RasterStateCullCCW = NULL;
	}

	m_FontD3DResVar = NULL;
	m_OffsetVar = NULL;
	m_CstColorVar = NULL;

	if( m_LineVertexBuffer )
	{
		ULONG rc = m_LineVertexBuffer->Release();
		assert( rc==0 ); (void)rc;
		m_LineVertexBuffer = NULL;
	}
	if( m_RectVertexBuffer )
	{
		ULONG rc = m_RectVertexBuffer->Release();
		assert( rc==0 ); (void)rc;
		m_RectVertexBuffer = NULL;
	}
	if( m_TrianglesVertexBuffer )
	{
		ULONG rc = m_TrianglesVertexBuffer->Release();
		assert( rc==0 ); (void)rc;
		m_TrianglesVertexBuffer = NULL;
		m_TrianglesVertexBufferCount = 0;
	}
	if( m_LineRectVertexLayout )
	{
		ULONG rc = m_LineRectVertexLayout->Release();
		assert( rc==0 ); (void)rc;
		m_LineRectVertexLayout = NULL;
	}
	if( m_TextVertexLayout )
	{
		ULONG rc = m_TextVertexLayout->Release();
		assert( rc==0 ); (void)rc;
		m_TextVertexLayout = NULL;
	}
	if( m_Effect )
	{
		ULONG rc = m_Effect->Release();
		assert( rc==0 ); (void)rc;
		m_Effect = NULL;
	}

	if( m_D3DDev )
	{
		//unsigned int rc = m_D3DDev->Release();
		//assert( m_D3DDevInitialRefCount==rc ); (void)rc;
		m_D3DDev->Release();
		m_D3DDev = NULL;
	}

	// Unload D3D10
	UnloadDirect3D10(); // this is not a problem if it cannot be unloaded

	return 1;
}

//	---------------------------------------------------------------------------

void CTwGraphDirect3D10::BeginDraw(int _WndWidth, int _WndHeight)
{
	assert(m_Drawing==false && _WndWidth>0 && _WndHeight>0);
	m_Drawing = true;

	m_WndWidth	= _WndWidth;
	m_WndHeight = _WndHeight;
	m_OffsetX = m_OffsetY = 0;

	// save context
	m_State->Save();

	// Setup the viewport
	D3D10_VIEWPORT vp;
	vp.Width = _WndWidth;
	vp.Height = _WndHeight;
	vp.MinDepth = 0.0f;
	vp.MaxDepth = 1.0f;
	vp.TopLeftX = 0;
	vp.TopLeftY = 0;
	m_D3DDev->RSSetViewports(1, &vp);
	*static_cast<D3D10_VIEWPORT *>(m_ViewportInit) = vp;

	m_D3DDev->RSSetState(m_RasterState);

	m_D3DDev->OMSetDepthStencilState(m_DepthStencilState, 0);
	float blendFactors[4] = { 1, 1, 1, 1 };
	m_D3DDev->OMSetBlendState(m_BlendState, blendFactors, 0xffffffff);
}

//	---------------------------------------------------------------------------

void CTwGraphDirect3D10::EndDraw()
{
	m_D3DDev->RSSetState(NULL);
	m_D3DDev->OMSetDepthStencilState(NULL, 0);
	m_D3DDev->OMSetBlendState(NULL, NULL, 0xffffffff);

	assert(m_Drawing==true);
	m_Drawing = false;

	// restore context
	m_State->Restore();
}

//	---------------------------------------------------------------------------

bool CTwGraphDirect3D10::IsDrawing()
{
	return m_Drawing;
}

//	---------------------------------------------------------------------------

void CTwGraphDirect3D10::Restore()
{
	if( m_State )
	{
		if( m_State->m_StateBlock )
		{
			UINT rc = m_State->m_StateBlock->Release();
			assert( rc==0 ); (void)rc;
			m_State->m_StateBlock = NULL;
		}
	}

	UnbindFont(m_D3DDev, m_FontD3DResVar, m_FontD3DTexRV);
	m_FontD3DTexRV = NULL;

	m_FontTex = NULL;
}


//	---------------------------------------------------------------------------

static inline float ToNormScreenX(int x, int wndWidth)
{
	return 2.0f*((float)x-0.5f)/wndWidth - 1.0f;
}

static inline float ToNormScreenY(int y, int wndHeight)
{
	return 1.0f - 2.0f*((float)y-0.5f)/wndHeight;
}

static inline color32 ToR8G8B8A8(color32 col)
{
	return (col & 0xff00ff00) | ((col>>16) & 0xff) | ((col<<16) & 0xff0000);
}

//	---------------------------------------------------------------------------

void CTwGraphDirect3D10::DrawLine(int _X0, int _Y0, int _X1, int _Y1, color32 _Color0, color32 _Color1, bool _AntiAliased)
{
	assert(m_Drawing==true);

	float x0 = ToNormScreenX(_X0 + m_OffsetX, m_WndWidth);
	float y0 = ToNormScreenY(_Y0 + m_OffsetY, m_WndHeight);
	float x1 = ToNormScreenX(_X1 + m_OffsetX, m_WndWidth);
	float y1 = ToNormScreenY(_Y1 + m_OffsetY, m_WndHeight);

	CLineRectVtx *vertices = NULL;
	HRESULT hr = m_LineVertexBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, (void **)&vertices);
	if( SUCCEEDED(hr) )
	{
		// Fill vertex buffer
		vertices[0].m_Pos[0] = x0;
		vertices[0].m_Pos[1] = y0;
		vertices[0].m_Pos[2] = 0;
		vertices[0].m_Color = ToR8G8B8A8(_Color0);
		vertices[1].m_Pos[0] = x1;
		vertices[1].m_Pos[1] = y1;
		vertices[1].m_Pos[2] = 0;
		vertices[1].m_Color = ToR8G8B8A8(_Color1);

		m_LineVertexBuffer->Unmap();

		if( _AntiAliased )
			m_D3DDev->RSSetState(m_RasterStateAntialiased);

		// Reset shader globals
		float offsetVec[4] = { 0, 0, 0, 0 };
		if( m_OffsetVar )
			m_OffsetVar->SetFloatVector(offsetVec);
		float colorVec[4] = { 1, 1, 1, 1 };
		if( m_CstColorVar )
			m_CstColorVar->SetFloatVector(colorVec);

		// Set the input layout
		m_D3DDev->IASetInputLayout(m_LineRectVertexLayout);

		// Set vertex buffer
		UINT stride = sizeof(CLineRectVtx);
		UINT offset = 0;
		m_D3DDev->IASetVertexBuffers(0, 1, &m_LineVertexBuffer, &stride, &offset);

		// Set primitive topology
		m_D3DDev->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_LINELIST);

		// Render the line
		D3D10_TECHNIQUE_DESC techDesc;
		m_LineRectTech->GetDesc(&techDesc);
		for(UINT p=0; p<techDesc.Passes; ++p)
		{
			m_LineRectTech->GetPassByIndex(p)->Apply(0);
			m_D3DDev->Draw(2, 0);
		}

		if( _AntiAliased )
			m_D3DDev->RSSetState(m_RasterState); // restore default raster state
	}
}

//	---------------------------------------------------------------------------

void CTwGraphDirect3D10::DrawRect(int _X0, int _Y0, int _X1, int _Y1, color32 _Color00, color32 _Color10, color32 _Color01, color32 _Color11)
{
	assert(m_Drawing==true);

	// border adjustment
	if(_X0<_X1)
		++_X1;
	else if(_X0>_X1)
		++_X0;
	if(_Y0<_Y1)
		++_Y1;
	else if(_Y0>_Y1)
		++_Y0;

	float x0 = ToNormScreenX(_X0 + m_OffsetX, m_WndWidth);
	float y0 = ToNormScreenY(_Y0 + m_OffsetY, m_WndHeight);
	float x1 = ToNormScreenX(_X1 + m_OffsetX, m_WndWidth);
	float y1 = ToNormScreenY(_Y1 + m_OffsetY, m_WndHeight);

	CLineRectVtx *vertices = NULL;
	HRESULT hr = m_RectVertexBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, (void **)&vertices);
	if( SUCCEEDED(hr) )
	{
		// Fill vertex buffer
		vertices[0].m_Pos[0] = x0;
		vertices[0].m_Pos[1] = y0;
		vertices[0].m_Pos[2] = 0;
		vertices[0].m_Color = ToR8G8B8A8(_Color00);
		vertices[1].m_Pos[0] = x1;
		vertices[1].m_Pos[1] = y0;
		vertices[1].m_Pos[2] = 0;
		vertices[1].m_Color = ToR8G8B8A8(_Color10);
		vertices[2].m_Pos[0] = x0;
		vertices[2].m_Pos[1] = y1;
		vertices[2].m_Pos[2] = 0;
		vertices[2].m_Color = ToR8G8B8A8(_Color01);
		vertices[3].m_Pos[0] = x1;
		vertices[3].m_Pos[1] = y1;
		vertices[3].m_Pos[2] = 0;
		vertices[3].m_Color = ToR8G8B8A8(_Color11);

		m_RectVertexBuffer->Unmap();

		// Reset shader globals
		float offsetVec[4] = { 0, 0, 0, 0 };
		if( m_OffsetVar )
			m_OffsetVar->SetFloatVector(offsetVec);
		float colorVec[4] = { 1, 1, 1, 1 };
		if( m_CstColorVar )
			m_CstColorVar->SetFloatVector(colorVec);

		// Set the input layout
		m_D3DDev->IASetInputLayout(m_LineRectVertexLayout);

		// Set vertex buffer
		UINT stride = sizeof(CLineRectVtx);
		UINT offset = 0;
		m_D3DDev->IASetVertexBuffers(0, 1, &m_RectVertexBuffer, &stride, &offset);

		// Set primitive topology
		m_D3DDev->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);

		// Render the rect
		D3D10_TECHNIQUE_DESC techDesc;
		m_LineRectTech->GetDesc(&techDesc);
		for(UINT p=0; p<techDesc.Passes; ++p)
		{
			m_LineRectTech->GetPassByIndex(p)->Apply(0);
			m_D3DDev->Draw(4, 0);
		}
	}
}

//	---------------------------------------------------------------------------

void *CTwGraphDirect3D10::NewTextObj()
{
	CTextObj *textObj = new CTextObj;
	memset(textObj, 0, sizeof(CTextObj));
	return textObj;
}

//	---------------------------------------------------------------------------

void CTwGraphDirect3D10::DeleteTextObj(void *_TextObj)
{
	assert(_TextObj!=NULL);
	CTextObj *textObj = static_cast<CTextObj *>(_TextObj);
	if( textObj->m_TextVertexBuffer )
		textObj->m_TextVertexBuffer->Release();
	if( textObj->m_BgVertexBuffer )
		textObj->m_BgVertexBuffer->Release();
	memset(textObj, 0, sizeof(CTextObj));
	delete textObj;
}

//	---------------------------------------------------------------------------

void CTwGraphDirect3D10::BuildText(void *_TextObj, const std::string *_TextLines, color32 *_LineColors, color32 *_LineBgColors, int _NbLines, const CTexFont *_Font, int _Sep, int _BgWidth)
{
	assert(m_Drawing==true);
	assert(_TextObj!=NULL);
	assert(_Font!=NULL);

	if( _Font != m_FontTex )
	{
		UnbindFont(m_D3DDev, m_FontD3DResVar, m_FontD3DTexRV);
		m_FontD3DTexRV = BindFont(m_D3DDev, m_FontD3DResVar, _Font);
		m_FontTex = _Font;
	}

	int nbTextVerts = 0;
	int line;
	for( line=0; line<_NbLines; ++line )
		nbTextVerts += 6 * (int)_TextLines[line].length();
	int nbBgVerts = 0;
	if( _BgWidth>0 )
		nbBgVerts = _NbLines*6;

	CTextObj *textObj = static_cast<CTextObj *>(_TextObj);
	textObj->m_LineColors = (_LineColors!=NULL);
	textObj->m_LineBgColors = (_LineBgColors!=NULL);

	// (re)create text vertex buffer if needed, and map it
	CTextVtx *textVerts = NULL;
	if( nbTextVerts>0 )
	{
		if( textObj->m_TextVertexBuffer==NULL || textObj->m_TextVertexBufferSize<nbTextVerts )
		{
			if( textObj->m_TextVertexBuffer!=NULL )
			{
				ULONG rc = textObj->m_TextVertexBuffer->Release();
				assert( rc==0 ); (void)rc;
				textObj->m_TextVertexBuffer = NULL;
			}
			textObj->m_TextVertexBufferSize = nbTextVerts + 6*256; // add a reserve of 256 characters
			D3D10_BUFFER_DESC bd;
			bd.Usage = D3D10_USAGE_DYNAMIC;
			bd.ByteWidth = textObj->m_TextVertexBufferSize * sizeof(CTextVtx);
			bd.BindFlags = D3D10_BIND_VERTEX_BUFFER;
			bd.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
			bd.MiscFlags = 0;
			m_D3DDev->CreateBuffer(&bd, NULL, &textObj->m_TextVertexBuffer);
		}

		if( textObj->m_TextVertexBuffer!=NULL )
			textObj->m_TextVertexBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, (void **)&textVerts);
	}

	// (re)create bg vertex buffer if needed, and map it
	CLineRectVtx *bgVerts = NULL;
	if( nbBgVerts>0 )
	{
		if( textObj->m_BgVertexBuffer==NULL || textObj->m_BgVertexBufferSize<nbBgVerts )
		{
			if( textObj->m_BgVertexBuffer!=NULL )
			{
				ULONG rc = textObj->m_BgVertexBuffer->Release();
				assert( rc==0 ); (void)rc;
				textObj->m_BgVertexBuffer = NULL;
			}
			textObj->m_BgVertexBufferSize = nbBgVerts + 6*32; // add a reserve of 32 rects
			D3D10_BUFFER_DESC bd;
			bd.Usage = D3D10_USAGE_DYNAMIC;
			bd.ByteWidth = textObj->m_BgVertexBufferSize * sizeof(CLineRectVtx);
			bd.BindFlags = D3D10_BIND_VERTEX_BUFFER;
			bd.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
			bd.MiscFlags = 0;
			m_D3DDev->CreateBuffer(&bd, NULL, &textObj->m_BgVertexBuffer);
		}

		if( textObj->m_BgVertexBuffer!=NULL )
			textObj->m_BgVertexBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, (void **)&bgVerts);
	}

	int x, x1, y, y1, i, len;
	float px, px1, py, py1;
	unsigned char ch;
	const unsigned char *text;
	color32 lineColor = COLOR32_RED;
	CTextVtx vtx;
	vtx.m_Pos[2] = 0;
	CLineRectVtx bgVtx;
	bgVtx.m_Pos[2] = 0;
	int textVtxIndex = 0;
	int bgVtxIndex = 0;
	for( line=0; line<_NbLines; ++line )
	{
		x = 0;
		y = line * (_Font->m_CharHeight+_Sep);
		y1 = y+_Font->m_CharHeight;
		len = (int)_TextLines[line].length();
		text = (const unsigned char *)(_TextLines[line].c_str());
		if( _LineColors!=NULL )
			lineColor = ToR8G8B8A8(_LineColors[line]);

		if( textVerts!=NULL )
			for( i=0; i<len; ++i )
			{
				ch = text[i];
				x1 = x + _Font->m_CharWidth[ch];

				px	= ToNormScreenX(x,	m_WndWidth);
				py	= ToNormScreenY(y,	m_WndHeight);
				px1 = ToNormScreenX(x1, m_WndWidth);
				py1 = ToNormScreenY(y1, m_WndHeight);

				vtx.m_Color  = lineColor;

				vtx.m_Pos[0] = px;
				vtx.m_Pos[1] = py;
				vtx.m_UV [0] = _Font->m_CharU0[ch];
				vtx.m_UV [1] = _Font->m_CharV0[ch];
				textVerts[textVtxIndex++] = vtx;

				vtx.m_Pos[0] = px1;
				vtx.m_Pos[1] = py;
				vtx.m_UV [0] = _Font->m_CharU1[ch];
				vtx.m_UV [1] = _Font->m_CharV0[ch];
				textVerts[textVtxIndex++] = vtx;

				vtx.m_Pos[0] = px;
				vtx.m_Pos[1] = py1;
				vtx.m_UV [0] = _Font->m_CharU0[ch];
				vtx.m_UV [1] = _Font->m_CharV1[ch];
				textVerts[textVtxIndex++] = vtx;

				vtx.m_Pos[0] = px1;
				vtx.m_Pos[1] = py;
				vtx.m_UV [0] = _Font->m_CharU1[ch];
				vtx.m_UV [1] = _Font->m_CharV0[ch];
				textVerts[textVtxIndex++] = vtx;

				vtx.m_Pos[0] = px1;
				vtx.m_Pos[1] = py1;
				vtx.m_UV [0] = _Font->m_CharU1[ch];
				vtx.m_UV [1] = _Font->m_CharV1[ch];
				textVerts[textVtxIndex++] = vtx;

				vtx.m_Pos[0] = px;
				vtx.m_Pos[1] = py1;
				vtx.m_UV [0] = _Font->m_CharU0[ch];
				vtx.m_UV [1] = _Font->m_CharV1[ch];
				textVerts[textVtxIndex++] = vtx;

				x = x1;
			}

		if( _BgWidth>0 && bgVerts!=NULL )
		{
			if( _LineBgColors!=NULL )
				bgVtx.m_Color = ToR8G8B8A8(_LineBgColors[line]);
			else
				bgVtx.m_Color = ToR8G8B8A8(COLOR32_BLACK);

			px	= ToNormScreenX(-1, m_WndWidth);
			py	= ToNormScreenY(y,	m_WndHeight);
			px1 = ToNormScreenX(_BgWidth+1, m_WndWidth);
			py1 = ToNormScreenY(y1, m_WndHeight);

			bgVtx.m_Pos[0] = px;
			bgVtx.m_Pos[1] = py;
			bgVerts[bgVtxIndex++] = bgVtx;

			bgVtx.m_Pos[0] = px1;
			bgVtx.m_Pos[1] = py;
			bgVerts[bgVtxIndex++] = bgVtx;

			bgVtx.m_Pos[0] = px;
			bgVtx.m_Pos[1] = py1;
			bgVerts[bgVtxIndex++] = bgVtx;

			bgVtx.m_Pos[0] = px1;
			bgVtx.m_Pos[1] = py;
			bgVerts[bgVtxIndex++] = bgVtx;

			bgVtx.m_Pos[0] = px1;
			bgVtx.m_Pos[1] = py1;
			bgVerts[bgVtxIndex++] = bgVtx;

			bgVtx.m_Pos[0] = px;
			bgVtx.m_Pos[1] = py1;
			bgVerts[bgVtxIndex++] = bgVtx;
		}
	}
	assert( textVtxIndex==nbTextVerts );
	assert( bgVtxIndex==nbBgVerts );
	textObj->m_NbTextVerts = nbTextVerts;
	textObj->m_NbBgVerts = nbBgVerts;

	if( textVerts!=NULL )
		textObj->m_TextVertexBuffer->Unmap();
	if( bgVerts!=NULL )
		textObj->m_BgVertexBuffer->Unmap();
}

//	---------------------------------------------------------------------------

void CTwGraphDirect3D10::DrawText(void *_TextObj, int _X, int _Y, color32 _Color, color32 _BgColor)
{
	assert(m_Drawing==true);
	assert(_TextObj!=NULL);
	CTextObj *textObj = static_cast<CTextObj *>(_TextObj);
	float dx = 2.0f*(float)(_X + m_OffsetX)/m_WndWidth;
	float dy = -2.0f*(float)(_Y + m_OffsetY)/m_WndHeight;

	float offsetVec[4] = { 0, 0, 0, 0 };
	offsetVec[0] = dx;
	offsetVec[1] = dy;
	if( m_OffsetVar )
		m_OffsetVar->SetFloatVector(offsetVec);

	// Draw background
	if( textObj->m_NbBgVerts>=4 && textObj->m_BgVertexBuffer!=NULL )
	{
		float color[4];
		Color32ToARGBf(_BgColor, color+3, color+0, color+1, color+2);
		if( m_CstColorVar )
			m_CstColorVar->SetFloatVector(color);

		// Set the input layout
		m_D3DDev->IASetInputLayout(m_LineRectVertexLayout);

		// Set vertex buffer
		UINT stride = sizeof(CLineRectVtx);
		UINT offset = 0;
		m_D3DDev->IASetVertexBuffers(0, 1, &textObj->m_BgVertexBuffer, &stride, &offset);

		// Set primitive topology
		m_D3DDev->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

		// Render the bg rectangles
		ID3D10EffectTechnique *tech;
		if( _BgColor!=0 || !textObj->m_LineBgColors ) // use a constant bg color
			tech = m_LineRectCstColorTech;
		else // use vertex buffer colors
			tech = m_LineRectTech;
		D3D10_TECHNIQUE_DESC techDesc;
		tech->GetDesc(&techDesc);
		for( UINT p=0; p<techDesc.Passes; ++p )
		{
			tech->GetPassByIndex(p)->Apply(0);
			m_D3DDev->Draw(textObj->m_NbBgVerts, 0);
		}
	}

	// Draw text
	if( textObj->m_NbTextVerts>=4 && textObj->m_TextVertexBuffer!=NULL )
	{
		float color[4];
		Color32ToARGBf(_Color, color+3, color+0, color+1, color+2);
		if( m_CstColorVar )
			m_CstColorVar->SetFloatVector(color);

		// Set the input layout
		m_D3DDev->IASetInputLayout(m_TextVertexLayout);

		// Set vertex buffer
		UINT stride = sizeof(CTextVtx);
		UINT offset = 0;
		m_D3DDev->IASetVertexBuffers(0, 1, &textObj->m_TextVertexBuffer, &stride, &offset);

		// Set primitive topology
		m_D3DDev->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

		// Render text
		ID3D10EffectTechnique *tech;
		if( _Color!=0 || !textObj->m_LineColors ) // use a constant color
			tech = m_TextCstColorTech;
		else // use vertex buffer colors
			tech = m_TextTech;
		D3D10_TECHNIQUE_DESC techDesc;
		tech->GetDesc(&techDesc);
		for( UINT p=0; p<techDesc.Passes; ++p )
		{
			tech->GetPassByIndex(p)->Apply(0);
			m_D3DDev->Draw(textObj->m_NbTextVerts, 0);
		}
	}
}

//	---------------------------------------------------------------------------

void CTwGraphDirect3D10::ChangeViewport(int _X0, int _Y0, int _Width, int _Height, int _OffsetX, int _OffsetY)
{
	if( _Width>0 && _Height>0 )
	{
		/* viewport changes screen coordinates, use scissor instead
		D3D10_VIEWPORT vp;
		vp.TopLeftX = _X0;
		vp.TopLeftY = _Y0;
		vp.Width = _Width;
		vp.Height = _Height;
		vp.MinDepth = 0;
		vp.MaxDepth = 1;
		m_D3DDev->RSSetViewports(1, &vp);
		*/

		m_ViewportAndScissorRects[0].left = _X0;
		m_ViewportAndScissorRects[0].right = _X0 + _Width - 1;
		m_ViewportAndScissorRects[0].top = _Y0;
		m_ViewportAndScissorRects[0].bottom = _Y0 + _Height - 1;
		if( RectIsFull(m_ViewportAndScissorRects[1]) )
			m_D3DDev->RSSetScissorRects(1, m_ViewportAndScissorRects); // viewport clipping only
		else
			m_D3DDev->RSSetScissorRects(2, m_ViewportAndScissorRects);

		m_OffsetX = _X0 + _OffsetX;
		m_OffsetY = _Y0 + _OffsetY;
	}
}

//	---------------------------------------------------------------------------

void CTwGraphDirect3D10::RestoreViewport()
{
	//m_D3DDev->RSSetViewports(1, static_cast<D3D10_VIEWPORT *>(m_ViewportInit));
	m_ViewportAndScissorRects[0] = FullRect;
	m_D3DDev->RSSetScissorRects(1, m_ViewportAndScissorRects+1); // scissor only

	m_OffsetX = m_OffsetY = 0;
}

//	---------------------------------------------------------------------------

void CTwGraphDirect3D10::SetScissor(int _X0, int _Y0, int _Width, int _Height)
{
	if( _Width>0 && _Height>0 )
	{
		m_ViewportAndScissorRects[1].left = _X0 - 2;
		m_ViewportAndScissorRects[1].right = _X0 + _Width - 3;
		m_ViewportAndScissorRects[1].top = _Y0 - 1;
		m_ViewportAndScissorRects[1].bottom = _Y0 + _Height - 1;
		if( RectIsFull(m_ViewportAndScissorRects[0]) )
			m_D3DDev->RSSetScissorRects(1, m_ViewportAndScissorRects+1); // no viewport clipping
		else
			m_D3DDev->RSSetScissorRects(2, m_ViewportAndScissorRects);
	}
	else
	{
		m_ViewportAndScissorRects[1] = FullRect;
		m_D3DDev->RSSetScissorRects(1, m_ViewportAndScissorRects); // apply viewport clipping only
	}
}

//	---------------------------------------------------------------------------

void CTwGraphDirect3D10::DrawTriangles(int _NumTriangles, int *_Vertices, color32 *_Colors, Cull _CullMode)
{
	assert(m_Drawing==true);

	if( _NumTriangles<=0 )
		return;

	if( m_TrianglesVertexBufferCount<3*_NumTriangles ) // force re-creation
	{
		if( m_TrianglesVertexBuffer!=NULL )
			m_TrianglesVertexBuffer->Release();
		m_TrianglesVertexBuffer = NULL;
		m_TrianglesVertexBufferCount = 0;
	}

	// DrawTriangles uses LineRect layout and technique

	if( m_TrianglesVertexBuffer==NULL )
	{
		// Create triangles vertex buffer
		D3D10_BUFFER_DESC bd;
		bd.Usage = D3D10_USAGE_DYNAMIC;
		bd.BindFlags = D3D10_BIND_VERTEX_BUFFER;
		bd.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
		bd.MiscFlags = 0;
		bd.ByteWidth = 3*_NumTriangles * sizeof(CLineRectVtx);
		HRESULT hr = m_D3DDev->CreateBuffer(&bd, NULL, &m_TrianglesVertexBuffer);
		if( SUCCEEDED(hr) )
			m_TrianglesVertexBufferCount = 3*_NumTriangles;
		else
		{
			m_TrianglesVertexBuffer = NULL;
			m_TrianglesVertexBufferCount = 0;
			return; // Problem: cannot create triangles VB
		}
	}
	assert( m_TrianglesVertexBufferCount>=3*_NumTriangles );
	assert( m_TrianglesVertexBuffer!=NULL );

	CLineRectVtx *vertices = NULL;
	HRESULT hr = m_TrianglesVertexBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, (void **)&vertices);
	if( SUCCEEDED(hr) )
	{
		// Fill vertex buffer
		for( int i=0; i<3*_NumTriangles; ++ i )
		{
			vertices[i].m_Pos[0] = ToNormScreenX(_Vertices[2*i+0] + m_OffsetX, m_WndWidth);
			vertices[i].m_Pos[1] = ToNormScreenY(_Vertices[2*i+1] + m_OffsetY, m_WndHeight);
			vertices[i].m_Pos[2] = 0;
			vertices[i].m_Color = ToR8G8B8A8(_Colors[i]);
		}
		m_TrianglesVertexBuffer->Unmap();

		// Reset shader globals
		float offsetVec[4] = { 0, 0, 0, 0 };
		if( m_OffsetVar )
			m_OffsetVar->SetFloatVector(offsetVec);
		float colorVec[4] = { 1, 1, 1, 1 };
		if( m_CstColorVar )
			m_CstColorVar->SetFloatVector(colorVec);

		// Set the input layout
		m_D3DDev->IASetInputLayout(m_LineRectVertexLayout);

		// Set vertex buffer
		UINT stride = sizeof(CLineRectVtx);
		UINT offset = 0;
		m_D3DDev->IASetVertexBuffers(0, 1, &m_TrianglesVertexBuffer, &stride, &offset);

		// Set primitive topology
		m_D3DDev->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

		if( _CullMode==CULL_CW )
			m_D3DDev->RSSetState(m_RasterStateCullCW);
		else if( _CullMode==CULL_CCW )
			m_D3DDev->RSSetState(m_RasterStateCullCCW);

		// Render the triangles
		D3D10_TECHNIQUE_DESC techDesc;
		m_LineRectTech->GetDesc(&techDesc);
		for(UINT p=0; p<techDesc.Passes; ++p)
		{
			m_LineRectTech->GetPassByIndex(p)->Apply(0);
			m_D3DDev->Draw(3*_NumTriangles, 0);
		}

		if( _CullMode==CULL_CW || _CullMode==CULL_CCW )
			m_D3DDev->RSSetState(m_RasterState); // restore default raster state

		// Unset vertex buffer
		ID3D10Buffer *vb = NULL;
		m_D3DDev->IASetVertexBuffers(0, 1, &vb, &stride, &offset);
	}
}

//	---------------------------------------------------------------------------
}
#endif // !defined ANT_TW_NO_D3D10
